home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / effects / qtshoweffect / qtshoweffect.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  58.6 KB  |  1,876 lines

  1. //////////
  2. //
  3. //    File:        QTShowEffect.c
  4. //
  5. //    Contains:    Code to generate a QuickTime movie with a QuickTime video effect in it,
  6. //                and to preview an effect on zero, one, or two pictures.
  7. //
  8. //    Written by:    Tim Monroe
  9. //                Based on existing ShowEffect code written by Dan Crow,
  10. //                which was adapted from Peter Hoddie's original DoEffect code.
  11. //
  12. //    Copyright:    © 1996-1999 by Apple Computer, Inc., all rights reserved.
  13. //
  14. //    Change History (most recent first):
  15. //
  16. //       <32>         05/24/99    rtm        fixed problem that caused image buffers to get corrupted on certain operations;
  17. //                                    we were not careful in our locking and unlocking the image pixmaps, so sometimes
  18. //                                    they would get unlocked and remain unlocked; now we lock the image pixmaps for
  19. //                                    gGW1 and gGW2 when the GWorlds are created, and then we leave them locked forever
  20. //       <31>         05/03/99    rtm        made call to GetCCursor cross-platform again; changed name of main effects
  21. //                                    display window; removed most support for compound effects; converted some
  22. //                                    hard-coded strings into constants; on Windows, made main effects display window
  23. //                                    a child of ghWndMDIClient (still some remaining cosmetic glitches, tho')
  24. //       <30>         03/23/99    rtm        started to add support for compound effects: renamed _CreateTwoSourceEffectDescription
  25. //                                    as _CreateEffectDescription
  26. //       <29>         03/02/99    rtm        added support for MakeImageDescriptionForEffect (QT 4.0 and later)
  27. //       <28>         02/04/99    rtm        made call to GetCCursor Mac-only (crashes on Windows); filed bug report
  28. //       <27>         09/30/98    rtm        tweaked call to AddMovieResource to create single-fork movies
  29. //       <26>         05/21/98    rtm        removed conditionalized support for compound effects
  30. //       <25>         05/20/98    rtm        changed stacked calls to NewGWorld to a single QTNewGWorld call in the
  31. //                                    functions QTEffects_GetPictResourceAsGWorld and QTEffects_GetPictureAsGWorld;
  32. //                                    fixed crashing bug in QTEffects_HandleEffectsDialogEvents by adding the test
  33. //                                    "&& (gSubPanelPopUpControl != NULL)" in control handle test
  34. //       <24>         04/23/98    rtm        changed gw->portPixMap to GetGWorldPixMap in CopyBits calls
  35. //       <23>         03/24/98    rtm        fixed minor problems with subpanels in custom dialog box
  36. //       <22>         03/23/98    rtm        added support for subpanels in custom dialog box
  37. //       <21>         03/09/98    rtm        changed kEffectSourceName constant in QTEffects_AddTrackReferenceToInputMap
  38. //                                    back to kEffectDataSourceType; now "Build Movie" works again
  39. //       <20>         03/06/98    rtm        moved custom dialog item handling to before the call to
  40. //                                    ImageCodecIsStandardParameterDialogEvent in QTEffects_HandleEffectsDialogEvents
  41. //       <19>         03/02/98    rtm        further clean-up on Windows
  42. //       <18>         02/26/98    rtm        added QTEffects_HandleEffectsDialogEvents, QTEffects_EffectsDialogCallback,
  43. //                                    and QTEffects_CustomDialogWndProc to properly handle Windows dialog boxes;
  44. //                                    miscellaneous other clean-up; added QTUtils_SetMovieFileLoopingInfo to
  45. //                                    QTEffects_CreateEffectsMovie
  46. //       <17>         02/21/98    rtm        added palindrome looping and stepping backward
  47. //       <16>         02/20/98    rtm        revised custom dialog box handling; now works on Windows (yippee!)
  48. //       <15>         02/18/98    rtm        removed support for thumbnail movies
  49. //       <14>         02/17/98    rtm        conditionalized support for compound effects (not yet implemented by QT 3.0)
  50. //       <13>         02/16/98    rtm        added QTEffects_HandleEffectsWindowEvents and *Messages for platform-specific
  51. //                                    event/message handling for main effects window
  52. //       <12>         02/13/98    rtm        reworked QTEffects_DrawEffectsWindow to support stepping through an effect
  53. //       <11>         02/12/98    rtm        removed all support for Preferences dialog box (Settings menu is enough)
  54. //       <10>         02/11/98    rtm        reworked QTEffects_CreateTwoSourceEffectDescription to return an effect
  55. //                                    description instead of setting gCurrentState.fEffectDescription directly;
  56. //                                    began adding support for compound effects
  57. //       <9>         02/10/98    rtm        added call to WriteResource to QTEffects_AddListOfEffects
  58. //       <8>         02/07/98    rtm        rewrote QTEffects_AddListOfEffects to use QTGetEffectsList
  59. //       <7>         02/04/98    rtm        fixed problems with custom dialog boxes; see the discussion in the
  60. //                                    function QTEffects_LetUserCustomizeEffect for details
  61. //       <6>         02/03/98    rtm        modified QTEffects_DrawEffectsWindow and QTEffects_AddVideoTrackFromGWorld
  62. //                                    to copy image(s) from existing GWorld(s); added ability to read pictures
  63. //                                    from files (in addition to resources) using graphics importer routines
  64. //       <5>         01/31/98    rtm        added Settings menu to replace Preferences dialog box
  65. //       <4>         01/30/98    rtm        fixed video track durations; now everything works fine on MacOS
  66. //       <3>         12/19/97    rtm        added gModalFilterUPP to ModalDialog so that the Select Effect
  67. //                                    dialog box can be moved
  68. //       <2>         12/18/97    rtm        continued clean-up: removed unused global variables and added
  69. //                                    constants for dialog box item indices
  70. //       <1>         12/15/97    rtm        first file; integrated existing code with shell framework
  71. //       
  72. //    This file defines functions that display a QuickTime video effect as a transition from one picture
  73. //    to another, or a QuickTime video effect applied to a single picture. The user selects the effect
  74. //    using a simple dialog box with a pop-up menu; then the user selects effect parameters using either
  75. //    the standard effects parameter dialog box or a custom effects parameter dialog box. Once an effect
  76. //    and some parameters are selected, the user can:
  77. //
  78. //        * run the effect once through from beginning to end
  79. //        * run the effect continuously in a normal or palindrome loop
  80. //        * run a single step of the effect, either forward or backward
  81. //        * generate a QuickTime movie file containing the current effect
  82. //        * select the picture(s) to which the effect is applied
  83. //
  84. //    Here we use the low-level QuickTime video effects APIs, for greater control over the process. The low-level
  85. //    API includes functions beginning "ImageCodec", for example: ImageCodecCreateStandardParameterDialog.
  86. //    The main advantage to using the low-level calls is the ability to embed the effect parameter dialog items
  87. //    into a custom, application-specific dialog box. (We do however use the high-level function QTGetEffectsList
  88. //    when building a list of the available effects.)
  89. //    
  90. //    NOTE:
  91. //    If you set the compiler flag ALLOW_COMPOUND_EFFECTS to 1, then the Build Effect Movie menu item will add
  92. //    the film noise filter to whatever two-source effect you've already chosen when it builds the movie; this is
  93. //    intended to illustrate how to work with compound (or "stacked") effects. QTShowEffect does not yet provide
  94. //    a mechanism to allow the user to add a compound effect to the one shown in the main effects display window.
  95. //
  96. //////////
  97.  
  98. //////////
  99. //
  100. // TO DO:
  101. // + 
  102. //
  103. //////////
  104.  
  105. //////////
  106. //
  107. // header files
  108. //
  109. //////////
  110.  
  111. #include "QTShowEffect.h"
  112.  
  113.  
  114. //////////
  115. //
  116. // global variables
  117. //
  118. //////////
  119.  
  120. WindowPtr                    gMainWindow = NULL;                // the main effect-display window
  121. QTParameterDialog            gEffectsDialog = 0L;            // identifier for the standard parameter dialog box
  122. DialogPtr                    gCustomDialog = NULL;            // the dialog that incorporates the standard parameter dialog box user interface elements
  123. GWorldPtr                    gGW1 = NULL;                    // the GWorlds that hold the effect sources
  124. GWorldPtr                    gGW2 = NULL;
  125. ImageDescriptionHandle        gGW1Desc = NULL;                // the image descriptions of the GWorlds
  126. ImageDescriptionHandle        gGW2Desc = NULL;
  127. ComponentInstance            gCompInstance = NULL;            // the instance of the current effect component
  128. unsigned short                gLoopingState = kNormalLooping;    // the current looping state of effect display
  129. unsigned short                gCurrentDir = kForward;            // the current direction of effect display
  130. Boolean                        gUseStandardDialog = true;        // if true, use the standard effect parameter dialog box; if false, use a custom effect parameter dialog box
  131. Boolean                        gFastEffectDisplay = false;        // if true, the effect is run to completion immediately;
  132.                                                             // if false, the effect runs as tickled by the event loop or message stream
  133. PopUpMenuInformation        gSelectEffectPopup;                // holds information about the Select Effect popup menu
  134. StateInformation            gCurrentState;                    // holds information about the current state of effects processing
  135. int                            gNumberOfSteps = k30StepsCount;
  136. MenuHandle                    gSubPanelPopUpMenu = NULL;        // menu handle for subpanel pop-up menu in custom dialog box
  137. ControlHandle                gSubPanelPopUpControl = NULL;    // control handle for subpanel pop-up menu in custom dialog box
  138.  
  139. extern ModalFilterUPP        gModalFilterUPP;
  140.  
  141. #if TARGET_OS_WIN32
  142. char                        gEffectsWindowClassName[] = kEffectsWindowClassName;
  143. extern HANDLE                ghInst;                            // the instance of this application
  144. extern HWND                    ghWndMDIClient;                 // the MDI client window
  145. extern Boolean                gShuttingDown;
  146. #endif
  147.  
  148.  
  149. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  150. //
  151. // Application start-up and shut-down functions.
  152. //
  153. // Use these functions to initialize and tear down effects processing for this application.
  154. //
  155. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  156.  
  157. //////////
  158. //
  159. // QTEffects_Init
  160. // Do application-specific initialization for effects processing.
  161. //
  162. //////////
  163.  
  164. OSErr QTEffects_Init (void)
  165. {
  166.     CCrsrHandle            myCursor = NULL;
  167.     OSErr                myErr = noErr;
  168.  
  169. #if TARGET_OS_WIN32
  170.     HWND                myWindow;
  171. #endif    
  172.  
  173. #if TARGET_OS_MAC    
  174.     Rect                myRect;
  175.     DialogPtr            myDialog = NULL;
  176.  
  177.     // show a splash screen while we set up the default values and other such things
  178.     myDialog = GetNewDialog(kSplashDialogID, NULL, (WindowPtr)-1);
  179.     if (myDialog != NULL) {
  180.         MacSetPort((GrafPtr)myDialog);
  181.         MacShowWindow(myDialog);
  182.         DrawDialog(myDialog);
  183.     }
  184. #endif
  185.  
  186.     // get wristwatch cursor; this initialization might take a little while....
  187.     myCursor = GetCCursor(kWatchCursorResID);
  188.     if (myCursor != NULL)
  189.         SetCCursor(myCursor);
  190.     
  191.     // set up the initial state
  192.     gCurrentState.fSampleDescription = NULL;
  193.     gCurrentState.fEffectDescription = NULL;
  194.     gCurrentState.fEffectType        = kCrossFadeTransitionType;
  195.     gCurrentState.fShowingEffect     = false;
  196.     gCurrentState.fTime              = 0;
  197.     gCurrentState.fEffectSequenceID  = 0L;
  198.     gCurrentState.fTimeBase          = NULL;
  199.     
  200.     // create the pop-up menu for the Select Effect dialog box
  201.     myErr = QTEffects_InitializePopUpMenu(&gSelectEffectPopup);
  202.     if (myErr != noErr)
  203.         goto bail;
  204.     
  205.     // add items to the menu
  206.     myErr = QTEffects_AddListOfEffects();
  207.     if (myErr != noErr) {
  208. #if TARGET_OS_MAC    
  209.         ShowWarning("\pCannot run this application from a locked volume; please copy to a different disk. Quitting.", 0);
  210.         ExitToShell();
  211. #endif
  212. #if TARGET_OS_WIN32
  213.         PostQuitMessage(0);
  214. #endif
  215.     }
  216.         
  217.     // create the GWorlds containing the pictures that will act as the sources for the effect to be displayed;
  218.     // on application start-up, we'll use canned pictures in resources
  219.     myErr = QTEffects_GetPictResourceAsGWorld(kFirstPICTResID, kWidth, kHeight, kDepth, &gGW1);
  220.     if (myErr != noErr)
  221.         goto bail;
  222.         
  223.     myErr = QTEffects_GetPictResourceAsGWorld(kSecondPICTResID, kWidth, kHeight, kDepth, &gGW2);
  224.     if (myErr != noErr)
  225.         goto bail;
  226.         
  227.     // lock the pixmaps; henceforth, whenever we draw into or copy out of these GWorlds, we shall assume that
  228.     // the pixmaps have previously been locked
  229.     if (!LockPixels(GetGWorldPixMap(gGW1)))
  230.         goto bail;
  231.         
  232.     if (!LockPixels(GetGWorldPixMap(gGW2)))
  233.         goto bail;
  234.  
  235.     // ***create the main effects display window***
  236. #if TARGET_OS_MAC    
  237.     // on MacOS, we create a standard Macintosh window and keep track of it in the global variable gMainWindow
  238.     MacSetRect(&myRect, kWindowOffset, kWindowOffset, kWindowOffset + kWidth, kWindowOffset + kHeight);
  239.     gMainWindow = NewCWindow(NULL, &myRect, c2pstr(kEffectsWindowTitle), true, 0, (WindowPtr)-1, true, 0);
  240. #endif
  241.     
  242. #if TARGET_OS_WIN32    
  243.     // on Windows, we create an instance of a custom window class (gEffectsWindowClassName), associate a port
  244.     // with it, and then keep track of that port in the global variable gMainWindow
  245.     QTEffects_RegisterEffectsWindowClass(ghInst);
  246.     myWindow = CreateWindow(gEffectsWindowClassName, kEffectsWindowTitle,
  247.                             WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_CHILD | WS_SYSMENU,
  248.                             kWindowOffset, 
  249.                             kWindowOffset,
  250.                             kWidth + (2 * GetSystemMetrics(SM_CXBORDER)), 
  251.                             kHeight + (2 * GetSystemMetrics(SM_CXBORDER)) + GetSystemMetrics(SM_CYCAPTION) - 1,
  252.                             ghWndMDIClient,
  253.                             NULL, 
  254.                             ghInst, 
  255.                             NULL);
  256.     if (myWindow != NULL)
  257.         gMainWindow = (WindowPtr)CreatePortAssociation(myWindow, NULL, 0L);
  258.         
  259. #endif
  260.  
  261. bail:
  262.  
  263. #if TARGET_OS_MAC    
  264.     // close down the splash screen
  265.     if (myDialog != NULL)
  266.         DisposeDialog(myDialog);
  267. #endif
  268.  
  269.     // restore the cursor to the arrow
  270.     InitCursor();
  271.     if (myCursor != NULL)
  272.         DisposeCCursor(myCursor);
  273.         
  274.     // now prompt the user for the first effect
  275.     if ((gMainWindow != NULL) && (myErr == noErr))
  276.         HandleApplicationMenu(IDM_SELECT_EFFECT);
  277.  
  278.     return(myErr);
  279. }
  280.  
  281.  
  282. //////////
  283. //
  284. // QTEffects_Stop
  285. // Do application-specific deinitialization for effects processing.
  286. //
  287. //////////
  288.  
  289. OSErr QTEffects_Stop (void)
  290. {
  291.     OSErr                myErr = noErr;
  292.  
  293.     // close the main effects window
  294. #if TARGET_OS_WIN32
  295.     if (gMainWindow != NULL)
  296.         DestroyWindow(GetWindowReferenceFromPort(gMainWindow));
  297. #endif
  298.     
  299. #if TARGET_OS_MAC
  300.     if (gMainWindow != NULL)
  301.         DisposeWindow(gMainWindow);
  302. #endif
  303.  
  304.     // deallocate any global storage
  305.     if (gGW1Desc != NULL)
  306.         DisposeHandle((Handle)gGW1Desc);
  307.         
  308.     if (gGW2Desc != NULL)
  309.         DisposeHandle((Handle)gGW2Desc);
  310.  
  311.     if (gGW1 != NULL)
  312.         DisposeGWorld(gGW1);
  313.         
  314.     if (gGW2 != NULL)
  315.         DisposeGWorld(gGW2);
  316.         
  317.     if (gCurrentState.fSampleDescription != NULL)
  318.         DisposeHandle((Handle)gCurrentState.fSampleDescription);
  319.         
  320.     if (gCurrentState.fEffectDescription != NULL)
  321.         QTDisposeAtomContainer(gCurrentState.fEffectDescription);
  322.         
  323.     if (gCurrentState.fEffectSequenceID != 0L)
  324.         CDSequenceEnd(gCurrentState.fEffectSequenceID);
  325.         
  326.     if (gCurrentState.fTimeBase != NULL)
  327.         DisposeTimeBase(gCurrentState.fTimeBase);
  328.         
  329.     if (gCompInstance != NULL)
  330.         CloseComponent(gCompInstance);
  331.  
  332.     return(myErr);
  333. }
  334.  
  335.  
  336. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  337. //
  338. // Effects window functions.
  339. //
  340. // Use these functions to set up and manage the main effects window displayed by this application.
  341. //
  342. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  343.  
  344. //////////
  345. //
  346. // QTEffects_ProcessEffect
  347. // Play one or more steps of the current effect.
  348. // 
  349. //////////
  350.  
  351. void QTEffects_ProcessEffect (void)
  352. {
  353.     OSErr        myErr = noErr;
  354.     
  355.     // if we are in "fast mode", play the effect forward thru to completion
  356.     if (gCurrentState.fShowingEffect && gFastEffectDisplay) {
  357.         for (gCurrentState.fTime = 1; gCurrentState.fTime <= gNumberOfSteps; gCurrentState.fTime++) {
  358.             myErr = QTEffects_RunEffect(gCurrentState.fTime);
  359.             if (myErr != noErr)
  360.                 return;
  361.         }
  362.         
  363.         gCurrentState.fShowingEffect = false;
  364.     }
  365.     
  366.     // otherwise, show the next step of the effect
  367.     if (gCurrentState.fShowingEffect && !gFastEffectDisplay) {
  368.     
  369.         if (gCurrentDir == kForward) {
  370.             gCurrentState.fTime++;
  371.             if (gCurrentState.fTime > gNumberOfSteps) {
  372.                 switch (gLoopingState) {
  373.                     case kNoLooping:
  374.                         gCurrentState.fTime = 0;
  375.                         gCurrentDir = kForward;
  376.                         gCurrentState.fShowingEffect = false;
  377.                         break;
  378.                     case kNormalLooping:
  379.                         gCurrentState.fTime = 0;
  380.                         gCurrentDir = kForward;
  381.                         gCurrentState.fShowingEffect = true;
  382.                         break;
  383.                     case kPalindromeLooping:
  384.                         gCurrentState.fTime = gNumberOfSteps;
  385.                         gCurrentDir = kBackward;
  386.                         gCurrentState.fShowingEffect = true;
  387.                         break;
  388.                 }
  389.             }
  390.         } else {
  391.             gCurrentState.fTime--;
  392.             if (gCurrentState.fTime < 1) {
  393.                 switch (gLoopingState) {
  394.                     case kNoLooping:
  395.                         gCurrentState.fTime = 0;
  396.                         gCurrentDir = kForward;
  397.                         gCurrentState.fShowingEffect = false;
  398.                         break;
  399.                     case kPalindromeLooping:
  400.                     case kNormalLooping:        // (this should never actually happen, since we're going backward)
  401.                         gCurrentState.fTime = 0;
  402.                         gCurrentDir = kForward;
  403.                         gCurrentState.fShowingEffect = true;
  404.                         break;
  405.                 }
  406.             }
  407.         }
  408.         
  409.         // run the next step of the effect
  410.         myErr = QTEffects_RunEffect(gCurrentState.fTime);
  411.         if (myErr != noErr)
  412.             return;
  413.  
  414.         if (gCurrentState.fSteppingEffect)
  415.             gCurrentState.fShowingEffect = false;
  416.     }
  417. }
  418.  
  419.  
  420. //////////
  421. //
  422. // QTEffects_DrawEffectsWindow
  423. // Draw the contents of the main effects window.
  424. // 
  425. //////////
  426.  
  427. void QTEffects_DrawEffectsWindow (void)
  428. {    
  429.     if ((gCurrentState.fEffectSequenceID != 0L) && (gCurrentState.fTime != 1)) {
  430.         QTEffects_RunEffect(gCurrentState.fTime);
  431.     } else {
  432.         // if we haven't set up an effect yet (which presumably happens only when the application is starting up)
  433.         // or if we're at the first frame, just copy the first source image into the window
  434.         CGrafPtr         mySavedPort = NULL;
  435.         GDHandle        mySavedGDevice = NULL;
  436.         PixMapHandle    myPixMap = NULL;
  437.         
  438.         // get the current port and device
  439.         GetGWorld(&mySavedPort, &mySavedGDevice);
  440.         
  441.         MacSetPort((GrafPtr)gMainWindow);
  442.  
  443.         myPixMap = GetGWorldPixMap(gGW1);
  444.         // note that the image pixmap was locked when gGW1 was created, so we don't need to do it again here
  445.         
  446.         // copy the image from the first source GWorld into the main window
  447.         CopyBits(    (BitMap *)(*myPixMap),
  448.                     (BitMap *)&(gMainWindow->portBits),
  449.                     &(gGW1->portRect),
  450.                     &(gMainWindow->portRect),
  451.                     srcCopy, NULL);
  452.  
  453.         // restore the original port and device
  454.         SetGWorld(mySavedPort, mySavedGDevice);
  455.     }
  456. }
  457.  
  458.  
  459. #if TARGET_OS_MAC
  460. //////////
  461. //
  462. // QTEffects_HandleEffectsWindowEvents
  463. // Handle Macintosh events for the main effects window.
  464. // 
  465. //////////
  466.  
  467. Boolean QTEffects_HandleEffectsWindowEvents (EventRecord *theEvent)
  468. {
  469.     Boolean            isHandled = false;
  470.     short            myWindowPart;
  471.     WindowPtr        myWindow;
  472.  
  473.     switch (theEvent->what) {
  474.         case mouseDown:
  475.             myWindowPart = MacFindWindow(theEvent->where, &myWindow);
  476.             if (myWindow != gMainWindow)
  477.                 break;
  478.                 
  479.             switch (myWindowPart) {
  480.                 case inDrag:
  481.                     DragWindow(myWindow, theEvent->where, &((**GetGrayRgn()).rgnBBox));
  482.                     isHandled = true;
  483.                     break;
  484.                     
  485.                 case inContent:
  486.                     SelectWindow(myWindow);
  487.                     MacSetPort(myWindow);
  488.                     HandleContentClick(myWindow, theEvent);
  489.                     isHandled = true;
  490.                     break;
  491.                     
  492.                 case inGoAway:
  493.                     // if the close box on the main window is clicked, dispose the window and quit the application
  494.                     if (TrackGoAway(myWindow, theEvent->where))
  495.                         if (myWindow == gMainWindow) {
  496.                             DisposeWindow(myWindow);
  497.                             gMainWindow = NULL;
  498.                             QuitFramework();
  499.                         }
  500.                     isHandled = true;
  501.                     break;
  502.                     
  503.             } // end switch(myWindowPart)
  504.             break;
  505.     
  506.         case updateEvt:
  507.             myWindow = (WindowPtr)theEvent->message;
  508.             if (myWindow == gMainWindow) {
  509.                 DoUpdateWindow(myWindow, NULL);
  510.                 isHandled = true;
  511.             }
  512.             break;
  513.  
  514.         default:
  515.             break;
  516.     }
  517.     
  518.     return(isHandled);
  519. }
  520. #endif    // TARGET_OS_MAC
  521.  
  522.  
  523. #if TARGET_OS_WIN32
  524. //////////
  525. //
  526. // QTEffects_HandleEffectsWindowMessages
  527. // Handle Windows messages for the main effects window.
  528. // 
  529. //////////
  530.  
  531. LRESULT CALLBACK QTEffects_HandleEffectsWindowMessages (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
  532. {
  533.     PAINTSTRUCT        myPaintStruct;
  534.  
  535.     // run the next step(s) of the effect
  536.     if (!gShuttingDown)
  537.         QTEffects_ProcessEffect();
  538.         
  539.     switch (theMessage) {
  540.  
  541.         case WM_PAINT:
  542.             BeginPaint(theWnd, &myPaintStruct);
  543.             QTEffects_DrawEffectsWindow();
  544.             EndPaint(theWnd, &myPaintStruct);
  545.             break;
  546.  
  547.         case WM_NCLBUTTONDOWN:
  548.         case WM_NCRBUTTONDOWN:
  549.             SetForegroundWindow(theWnd);
  550.             break;
  551.             
  552.         case WM_LBUTTONDOWN:
  553.         case WM_RBUTTONDOWN:
  554.             SetForegroundWindow(theWnd);
  555.             HandleContentClick(theWnd, NULL);
  556.             return(0);
  557.  
  558.         case WM_CLOSE:
  559.             if (theWnd != NULL)
  560.                 DestroyWindow(theWnd);
  561.             gMainWindow = NULL;
  562.             QuitFramework();
  563.             break;
  564.  
  565.         case WM_DESTROY:
  566.             DestroyPortAssociation((CGrafPtr)gMainWindow);
  567.             break;
  568.  
  569.         default:
  570.             break;
  571.     }
  572.  
  573.     return(DefWindowProc(theWnd, theMessage, wParam, lParam));
  574. }
  575.  
  576.  
  577. //////////
  578. //
  579. // QTEffects_RegisterEffectsWindowClass
  580. // Register the window procedure for the main effects window.
  581. // 
  582. //////////
  583.  
  584. BOOL QTEffects_RegisterEffectsWindowClass (HANDLE hInstance)
  585. {
  586.     WNDCLASSEX            myWC;
  587.  
  588.     // register the main effect window class
  589.     myWC.cbSize        = sizeof(WNDCLASSEX);
  590.     myWC.style         = CS_HREDRAW | CS_VREDRAW;
  591.     myWC.lpfnWndProc   = (WNDPROC)QTEffects_HandleEffectsWindowMessages;
  592.     myWC.cbClsExtra    = 0;
  593.     myWC.cbWndExtra    = 0;
  594.     myWC.hInstance     = hInstance;
  595.     myWC.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON));
  596.     myWC.hCursor       = LoadCursor(NULL, IDC_ARROW);
  597.     myWC.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  598.     myWC.lpszMenuName  = NULL;
  599.     myWC.lpszClassName = gEffectsWindowClassName;
  600.     myWC.hIconSm       = LoadImage(hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 16, 16, 0);
  601.                                  
  602.     if (!RegisterClassEx(&myWC)) {
  603.         if (!RegisterClass((LPWNDCLASS)&myWC.style))
  604.             return(false);
  605.     }
  606.     
  607.     return(true);
  608. }
  609. #endif    // TARGET_OS_WIN32
  610.  
  611.  
  612. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  613. //
  614. // Dialog utilities.
  615. //
  616. // Use these functions to set up and manage the dialog boxes displayed by this application.
  617. //
  618. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  619.  
  620. //////////
  621. //
  622. // QTEffects_LetUserChooseEffect
  623. // Display a dialog box that contains a pop-up menu of all the effects currently available,
  624. // and allow the user to select an effect.
  625. // 
  626. //////////
  627.  
  628. OSErr QTEffects_LetUserChooseEffect (void)
  629. {
  630.     DialogPtr        myDialog;
  631.     short            myItem;
  632.     short            myType;
  633.     Handle            myItemHandle;
  634.     Rect            myRect;
  635.     int                myIndex;
  636.     OSErr            myErr = noErr;
  637.     
  638.     // get the dialog that lets the user select an effect component
  639.     myDialog = GetNewDialog(kSelectDialogID, NULL, (WindowPtr)-1);
  640.     if (myDialog == NULL)
  641.         goto bail;
  642.  
  643.     myErr = SetDialogDefaultItem(myDialog, kSelectButtonOK);
  644.     if (myErr != noErr)
  645.         goto bail;
  646.         
  647.     // find the currently selected effect in the list, so that it's showing when the dialog is displayed
  648.     for (myIndex = 0; myIndex < gSelectEffectPopup.fNumberOfItems; myIndex++) {
  649.         if (gSelectEffectPopup.fItemInfo[myIndex] == gCurrentState.fEffectType) {
  650.             GetDialogItem(myDialog, kSelectPopUpID, &myType, &myItemHandle, &myRect);
  651.             SetControlValue((ControlHandle)myItemHandle, myIndex + 1);
  652.         }
  653.     }
  654.     
  655.     // now show the dialog
  656.     MacShowWindow(myDialog);
  657.     MacSetPort((GrafPtr)myDialog);
  658.     
  659.     do {
  660.         ModalDialog(gModalFilterUPP, &myItem);
  661.     } while (myItem != kSelectButtonOK);
  662.     
  663.     // get the control handle for the pop-up menu and read its current value
  664.     GetDialogItem(myDialog, kSelectPopUpID, &myType, &myItemHandle, &myRect);
  665.     gSelectEffectPopup.fLastChosen = GetControlValue((ControlHandle)myItemHandle);
  666.         
  667.     // now the user has hit OK and gSelectEffectPopup.fLastChosen contains the item chosen.
  668.     gCurrentState.fEffectType = (OSType)gSelectEffectPopup.fItemInfo[gSelectEffectPopup.fLastChosen - 1];
  669.  
  670. bail:
  671.     if (myDialog != NULL)
  672.         DisposeDialog(myDialog);
  673.     
  674.     return(myErr);
  675. }
  676.  
  677.  
  678. //////////
  679. //
  680. // QTEffects_LetUserCustomizeEffect
  681. // Display a dialog box that allows the user to select non-default parameters for an effect.
  682. // 
  683. //////////
  684.  
  685. OSErr QTEffects_LetUserCustomizeEffect (QTAtomContainer theEffectDesc)
  686. {
  687.     ComponentDescription    myCD;
  688.     Component                myComponent = NULL;
  689.     QTAtomContainer            myParamDesc = NULL;
  690.     MenuHandle                myMenu = NULL;
  691.     OSErr                    myErr = noErr;
  692.     
  693.     // set up a component description
  694.     myCD.componentType            = decompressorComponentType;    // effects are image decompressor components
  695.     myCD.componentSubType        = gCurrentState.fEffectType;    // whichever subtype of effect we are looking for
  696.     myCD.componentManufacturer    = 0;
  697.     myCD.componentFlags            = 0;
  698.     myCD.componentFlagsMask        = 0;
  699.     
  700.     // if the component has been previously opened, close it (since we may be customizing a different effect)
  701.     if (gCompInstance != NULL) {
  702.         CloseComponent(gCompInstance);
  703.         gCompInstance = NULL;
  704.     }
  705.     
  706.     // find the required component
  707.     myComponent = FindNextComponent(myComponent, &myCD);
  708.     if (myComponent == NULL)
  709.         return(paramErr);
  710.         
  711.     // open the component
  712.     gCompInstance = OpenComponent(myComponent);
  713.     
  714.     // get the list of parameters for the effect
  715.     myErr = ImageCodecGetParameterList(gCompInstance, &myParamDesc);
  716.     if (myErr != noErr)
  717.         goto bail;
  718.         
  719.     // if the user has chosen the standard dialog box, then display it...
  720.     if (gUseStandardDialog) {
  721.         // set up the dialog box
  722.         myErr = ImageCodecCreateStandardParameterDialog(
  723.                                     gCompInstance,
  724.                                     myParamDesc,
  725.                                     theEffectDesc,
  726.                                     0,                            // dialog options
  727.                                     NULL,                        // no existing dialog
  728.                                     0,                            // no existing user item
  729.                                     &gEffectsDialog);
  730.         if ((myErr != noErr) || (gEffectsDialog == 0L))
  731.             goto bail;
  732.             
  733.         gCustomDialog = NULL;
  734.         
  735.     } else {
  736.     // ...otherwise, create the dialog box as a part of a custom application-supplied dialog box
  737.     
  738.         // Get the application-supplied dialog box; a few words of explanation are appropriate here:        
  739.         // QuickTime expects this dialog box to be a color window, or the slider elements (and some
  740.         // other controls) will not draw correctly; accordingly, your application needs to include
  741.         // several resources that have the same ID as the dialog box resource (here, kCustomDialogID).
  742.         // For MacOS 8 and/or Appearance Manager compatibility, the application must also a resource
  743.         // of type 'dlgx', whose data (a long word) has at least the bits kDialogFlagsUseThemeBackground
  744.         // and kDialogFlagsUseThemeControls set; we'll also set the bit kDialogFlagsHandleMovableModal.
  745.         // So our entire 6-byte 'dlgx' resource is 00000000000D. (The first 2 bytes of the 'dlgx' resource
  746.         // are a version number, which we set to 0.) For vanilla System 7 compatibility, the application
  747.         // must include a resource of type 'dctb' whose ID is kCustomDialogID.
  748.  
  749.         gCustomDialog = GetNewDialog(kCustomDialogID, NULL, (WindowPtr)-1);
  750.         if (gCustomDialog != NULL) {
  751.         
  752.             // set up the dialog box
  753.             myErr = ImageCodecCreateStandardParameterDialog(
  754.                                     gCompInstance,
  755.                                     myParamDesc,
  756.                                     theEffectDesc,
  757.                                     0,                            // dialog options
  758.                                     gCustomDialog,                // the existing dialog
  759.                                     kCustomUserItemID,            // the existing user item
  760.                                     &gEffectsDialog);
  761.             if ((myErr != noErr) || (gEffectsDialog == 0L))
  762.                 goto bail;
  763.         
  764.             SetDialogDefaultItem(gCustomDialog, kCustomButtonOK);
  765.             
  766. #if TARGET_OS_WIN32
  767.             // on Windows, we need to explicitly tag the dialog box as a movable modal dialog box
  768.             SetDialogMovableModal(gCustomDialog); 
  769. #endif
  770.  
  771.             // see whether the current effect supports subpanels;
  772.             // if so, display a pop-up menu that contains the names of those subpanels
  773.             myErr = ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionGetSubPanelMenu, &myMenu);
  774.             if ((myErr == noErr) && (myMenu != NULL)) {
  775.                 short            myNumItems;
  776.                 short            myIndex;
  777.                 short            myItemKind;
  778.                 Handle            myItemHandle = NULL;
  779.                 Rect            myItemRect;
  780.                 
  781.                 // remove any existing menu items in gSubPanelPopUpMenu
  782.                 if (gSubPanelPopUpMenu != NULL) {
  783.                     myNumItems = CountMenuItems(gSubPanelPopUpMenu);
  784.                     for (myIndex = 0; myIndex < myNumItems; myIndex++)
  785.                         DeleteMenuItem(gSubPanelPopUpMenu, 1);
  786.                 }
  787.                 
  788.                 // get the control handle of the pop-up menu
  789.                 GetDialogItem(gCustomDialog, kCustomPopUpID, &myItemKind, &myItemHandle, &myItemRect);
  790.                 gSubPanelPopUpControl = (ControlHandle)myItemHandle;
  791.                 
  792.                 // get the menu handle of the pop-up menu
  793.                 gSubPanelPopUpMenu = (**(PopupPrivateDataHandle)(**gSubPanelPopUpControl).contrlData).mHandle;
  794.                 myNumItems = CountMenuItems(myMenu);
  795.                 for (myIndex = 1; myIndex <= myNumItems; myIndex++) {
  796.                     Str255        myString;
  797.                     
  798.                     // copy items into the pop-up menu
  799.                     GetMenuItemText(myMenu, myIndex, myString);
  800.                     MacInsertMenuItem(gSubPanelPopUpMenu, myString, myIndex + 1);
  801.                 }
  802.                 
  803.                 // set the minimum, maximum, and initial values of the pop-up menu
  804.                 SetControlMinimum(gSubPanelPopUpControl, 1);
  805.                 SetControlMaximum(gSubPanelPopUpControl, myNumItems);
  806.                 SetControlValue(gSubPanelPopUpControl, 1);
  807.                 ShowControl(gSubPanelPopUpControl);
  808.             }
  809.  
  810.             MacShowWindow(gCustomDialog);
  811.             MacSetPort(gCustomDialog);
  812.         }
  813.     }
  814.     
  815.     // now, the frontmost window is a standard or custom effects parameter dialog box;
  816.     // on the Mac, we call QTEffects_HandleEffectsDialogEvents in our main event loop
  817.     // to find and process events targeted at the effects parameter dialog box; on Windows,
  818.     // we need to use a different strategy: we install a modeless dialog callback procedure
  819.     // that is called internally by QTML
  820. #if TARGET_OS_WIN32
  821.     SetModelessDialogCallbackProc(FrontWindow(), (QTModelessCallbackUPP)QTEffects_EffectsDialogCallback);
  822.     QTMLSetWindowWndProc(FrontWindow(), QTEffects_CustomDialogWndProc);
  823. #endif
  824.  
  825. bail:
  826.     if (myParamDesc != NULL)
  827.         QTDisposeAtomContainer(myParamDesc);
  828.  
  829.     return(myErr);
  830. }
  831.  
  832.  
  833. #if TARGET_OS_WIN32
  834. //////////
  835. //
  836. // QTEffects_EffectsDialogCallback
  837. // This function is called by QTML when it processes events for the standard or custom effects parameter dialog box.
  838. // 
  839. //////////
  840.  
  841. static void QTEffects_EffectsDialogCallback (EventRecord *theEvent, DialogRef theDialog, DialogItemIndex theItemHit)
  842. {
  843.     QTParamDialogEventRecord    myRecord;
  844.  
  845.     myRecord.theEvent = theEvent;
  846.     myRecord.whichDialog = theDialog;
  847.     myRecord.itemHit = theItemHit;
  848.  
  849.     if (gEffectsDialog != 0L) {
  850.         ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionModelessCallback, &myRecord);
  851.     
  852.         // see if the event is meant for the effects parameter dialog box
  853.         QTEffects_HandleEffectsDialogEvents(theEvent, theItemHit);
  854.     }
  855. }
  856.  
  857.  
  858. //////////
  859. //
  860. // QTEffects_CustomDialogWndProc
  861. // Handle messages for the custom effects parameters dialog box.
  862. // 
  863. //////////
  864.  
  865. LRESULT CALLBACK QTEffects_CustomDialogWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
  866. {
  867.     EventRecord            myEvent = {0};
  868.     
  869.     // pass idle events thru to the dialog callback
  870.     if (theMessage == 0x7FFF)
  871.         QTEffects_EffectsDialogCallback(&myEvent, GetNativeWindowPort(theWnd), 0);
  872.  
  873.     return(DefWindowProc(theWnd, theMessage, wParam, lParam));
  874. }
  875. #endif
  876.  
  877.  
  878. //////////
  879. //
  880. // QTEffects_HandleEffectsDialogEvents
  881. // Process events that might be targeted at the standard or custom effects parameter dialog box.
  882. // Return true if the event was completely handled.
  883. // 
  884. //////////
  885.  
  886. Boolean QTEffects_HandleEffectsDialogEvents (EventRecord *theEvent, DialogItemIndex theItemHit)
  887. {
  888. #if TARGET_OS_MAC
  889. #pragma unused(theItemHit)
  890. #endif
  891.     Boolean            isHandled = false;
  892.     Boolean            myAcceptChanges = false;
  893.     Boolean            myCloseDialog = false;
  894.     OSErr            myErr = noErr;
  895.     
  896.     // handle events for our own items in the custom dialog box, if it exists;
  897.     // we need to do this BEFORE we pass the event to ImageCodecIsStandardParameterDialogEvent
  898.     if (gCustomDialog != NULL) {
  899.     
  900. #if TARGET_OS_WIN32
  901.         // on Windows, QTML has already done any required control tracking for us, and
  902.         // the theItemHit parameter contains the dialog item number of the selected item
  903.         switch (theItemHit) {
  904.             case kCustomPopUpID:
  905.                 ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionActivateSubPanel, (void *)GetControlValue(gSubPanelPopUpControl));
  906.                 break;
  907.             case kCustomButtonOK:
  908.                 myAcceptChanges = true;
  909.                 myCloseDialog = true;
  910.                 break;
  911.             default:
  912.                 break;
  913.         }
  914. #endif
  915.         
  916. #if TARGET_OS_MAC
  917.         // on the Mac, we need to do our own events processing
  918.         switch (theEvent->what) {
  919.             case mouseDown: {
  920.                 WindowPtr        myWindow;
  921.                 short            myPart;    
  922.                 ControlHandle    myControl;
  923.                 Point            myPoint;
  924.  
  925.                 myPart = MacFindWindow(theEvent->where, &myWindow);
  926.                 if ((myWindow == gCustomDialog) && (myPart == inContent)) {
  927.                     myPoint = theEvent->where;
  928.                     GlobalToLocal(&myPoint);
  929.                     myPart = FindControl(myPoint, myWindow, &myControl);
  930.                     
  931.                     // first, see if the user clicked in the subpanel pop-up menu
  932.                     if ((myControl == gSubPanelPopUpControl) && (gSubPanelPopUpControl != NULL)) {
  933.                         TrackControl(myControl, myPoint, (ControlActionUPP)-1);
  934.                         ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionActivateSubPanel, (void *)GetControlValue(myControl));
  935.                         return(true);
  936.                     }
  937.                     
  938.                     // next, see if the user clicked the OK button
  939.                     if (myPart == kControlButtonPart) {
  940.                         if (TrackControl(myControl, myPoint, NULL) != 0) {
  941.                             myAcceptChanges = true;
  942.                             myCloseDialog = true;
  943.                         }
  944.                     }
  945.                     
  946.                     // handle any other custom dialog box items here
  947.                     
  948.                 }
  949.                 break;
  950.             }
  951.  
  952.             case keyDown:
  953.             case autoKey:
  954.                 // handle the standard keyboard equivalents of OK button
  955.                 switch (theEvent->message & charCodeMask) {
  956.                     case kReturnKey:
  957.                     case kEnterKey:
  958.                         myAcceptChanges = true;
  959.                         myCloseDialog = true;
  960.                         break;
  961.                     default:
  962.                         break;
  963.                 }
  964.                 break;
  965.         }
  966. #endif
  967.         
  968.     }    // if (gCustomDialog != NULL)
  969.  
  970.     // pass the event to the standard effects parameter dialog box handler
  971.     myErr = ImageCodecIsStandardParameterDialogEvent(gCompInstance, theEvent, gEffectsDialog);
  972.     switch (myErr) {
  973.     
  974.         case codecParameterDialogConfirm:
  975.             // the user has selected the OK button of the standard (not custom) effects parameter dialog box            
  976.             myAcceptChanges = true;
  977.             myCloseDialog = true;
  978.             break;
  979.             
  980.         case userCanceledErr:
  981.             // the user has selected the Cancel button of the standard (not custom) effects parameter dialog box
  982.             myCloseDialog = true;
  983.             break;
  984.                         
  985.         case featureUnsupported:
  986.             // the event was *not* handled by ImageCodecIsStandardParameterDialogEvent;
  987.             // let the event be processed normally
  988.             break;
  989.         
  990.         case noErr:
  991.         default:
  992.             // the event was completely handled by ImageCodecIsStandardParameterDialogEvent
  993.             isHandled = true;
  994.             break;
  995.     }    
  996.  
  997.     if (myCloseDialog) {
  998.         // retrieve the values from the parameters dialog box
  999.         if (myAcceptChanges)
  1000.             ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionConfirmDialog, NULL);
  1001.             
  1002.         // remove the dialog box
  1003.         ImageCodecDismissStandardParameterDialog(gCompInstance, gEffectsDialog);
  1004.         
  1005.         if (gCustomDialog != NULL)
  1006.             DisposeDialog(gCustomDialog);
  1007.             
  1008.         gCustomDialog = NULL;
  1009.         gEffectsDialog = 0L;
  1010.         
  1011.         isHandled = true;
  1012.         
  1013.         // now prepare the decompression sequence
  1014.         myErr = QTEffects_SetUpEffectSequence();
  1015.     }
  1016.  
  1017.     return(isHandled);
  1018. }
  1019.  
  1020.  
  1021. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1022. //
  1023. // Pop-up menu utilities.
  1024. //
  1025. // Use these functions to set up and manage the global pop-up menu of effects used by this application.
  1026. // Here's our strategy: in our original resource file, the menu resource with ID kPopUpMenuID is empty;
  1027. // at run-time, we open the menu resource, remove any items from it, and then add a list of all effects
  1028. // to it. Then we write the new menu resource out to the resource file, whence GetNewDialog can find it.
  1029. // Very clever.
  1030. //
  1031. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1032.  
  1033. //////////
  1034. //
  1035. // QTEffects_InitializePopUpMenu
  1036. // Initialize the global pop-up effects menu.
  1037. // 
  1038. //////////
  1039.  
  1040. OSErr QTEffects_InitializePopUpMenu (PopUpMenuInformation *theMenuInfo)
  1041. {
  1042.     short            myIndex;
  1043.     short            myNumItems;
  1044.     OSErr            myErr = noErr;
  1045.  
  1046.     theMenuInfo->fMenu = MacGetMenu(kPopUpMenuID);
  1047.     if (theMenuInfo->fMenu == NULL) {
  1048.         myErr = ResError();
  1049.         goto bail;
  1050.     }
  1051.     
  1052.     // make sure the menu isn't purgeable
  1053.     HNoPurge((Handle)theMenuInfo->fMenu);
  1054.     
  1055.     // remove any existing menu items (since we're going to generate the effects list dynamically)
  1056.     myNumItems = CountMenuItems(theMenuInfo->fMenu);
  1057.     for (myIndex = 0; myIndex < myNumItems; myIndex++)
  1058.         DeleteMenuItem(theMenuInfo->fMenu, 1);
  1059.     
  1060.     theMenuInfo->fNumberOfItems = 0;
  1061.     theMenuInfo->fLastChosen = 1;
  1062.  
  1063. bail:
  1064.     return(myErr);
  1065. }
  1066.  
  1067.  
  1068. //////////
  1069. //
  1070. // QTEffects_AddItemToPopUpMenu
  1071. // Add an item to the global pop-up effects menu.
  1072. // 
  1073. //////////
  1074.  
  1075. OSErr QTEffects_AddItemToPopUpMenu (PopUpMenuInformation *theMenuInfo, char *theItemText, OSType theItemInfo)
  1076. {
  1077.     char    myCopy[256];
  1078.     OSErr    myErr = noErr;
  1079.  
  1080.     strcpy(myCopy, theItemText);
  1081.     strcpy(theMenuInfo->fMenuText[theMenuInfo->fNumberOfItems], myCopy);
  1082.     c2pstr(myCopy);
  1083.  
  1084.     MacInsertMenuItem(theMenuInfo->fMenu, (unsigned char *)myCopy, theMenuInfo->fNumberOfItems);
  1085.     theMenuInfo->fItemInfo[theMenuInfo->fNumberOfItems] = theItemInfo;
  1086.     theMenuInfo->fNumberOfItems++;
  1087.  
  1088.     return(myErr);
  1089. }
  1090.  
  1091.  
  1092. //////////
  1093. //
  1094. // QTEffects_AddListOfEffects
  1095. // Add a list of the available effects to the global pop-up effects menu.
  1096. // 
  1097. //////////
  1098.  
  1099. OSErr QTEffects_AddListOfEffects (void)
  1100. {
  1101.     QTAtomContainer            myEffectsList = NULL;
  1102.     short                    myNumEffects;
  1103.     short                    myIndex;
  1104.     OSErr                    myErr = noErr;
  1105.     
  1106.     // get a list of the available effects
  1107.     myErr = QTNewAtomContainer(&myEffectsList);
  1108.     if (myErr != noErr)
  1109.         goto bail;
  1110.         
  1111.     myErr = QTGetEffectsList(&myEffectsList, kNoMinNumSources, kNoMaxNumSources, 0L);
  1112.     if (myErr != noErr)
  1113.         goto bail;
  1114.  
  1115.     // the returned effects list contains (at least) two atoms for each available effect component,
  1116.     // a name atom and a type atom; happily, this list is already sorted alphabetically by effect name
  1117.     myNumEffects = QTCountChildrenOfType(myEffectsList, kParentAtomIsContainer, kEffectNameAtom);
  1118.     for (myIndex = 1; myIndex <= myNumEffects; myIndex++) {
  1119.         QTAtom                myNameAtom = 0L;
  1120.         QTAtom                myTypeAtom = 0L;
  1121.  
  1122.         myNameAtom = QTFindChildByIndex(myEffectsList, kParentAtomIsContainer, kEffectNameAtom, myIndex, NULL);
  1123.         myTypeAtom = QTFindChildByIndex(myEffectsList, kParentAtomIsContainer, kEffectTypeAtom, myIndex, NULL);
  1124.         if ((myNameAtom != 0L) && (myTypeAtom != 0L)) {
  1125.             char             myName[256];
  1126.             OSType             myType;
  1127.             long            mySize;
  1128.  
  1129.             // get the data from the type and name atoms
  1130.             QTCopyAtomDataToPtr(myEffectsList, myTypeAtom, false, sizeof(myType), &myType, NULL);
  1131.             QTCopyAtomDataToPtr(myEffectsList, myNameAtom, true, sizeof(myName), myName, &mySize);
  1132.             myName[mySize] = '\0';
  1133.             
  1134.             QTEffects_AddItemToPopUpMenu(&gSelectEffectPopup, myName, myType);
  1135.         }
  1136.     }
  1137.     
  1138.     // write the changed menu back into the resource file (whence GetNewDialog will load it);
  1139.     // first mark the menu resource as changed...
  1140.     ChangedResource((Handle)gSelectEffectPopup.fMenu);
  1141.     myErr = ResError();
  1142.     
  1143.     if (myErr == noErr) {
  1144.         // ...and then write the resource data out to the resource file
  1145.         WriteResource((Handle)gSelectEffectPopup.fMenu);
  1146.         myErr = ResError();
  1147.     }
  1148.     
  1149. bail:
  1150.     QTDisposeAtomContainer(myEffectsList);    
  1151.     return(myErr);
  1152. }
  1153.  
  1154.  
  1155. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1156. //
  1157. // Effects utilities.
  1158. //
  1159. // Use these functions to set up and run QuickTime video effects.
  1160. //
  1161. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1162.  
  1163. //////////
  1164. //
  1165. // QTEffects_CreateEffectDescription
  1166. // Create an effect description for zero, one, or two sources.
  1167. // 
  1168. // The effect description specifies which video effect is desired and the parameters for that effect.
  1169. // It also describes the source(s) for the effect. An effect description is simply an atom container
  1170. // that holds atoms with the appropriate information.
  1171. //
  1172. // Note that because we are creating an atom container, we must pass big-endian data (hence the calls
  1173. // to EndianU32_NtoB).
  1174. //
  1175. // The caller is responsible for disposing of the returned atom container, by calling QTDisposeAtomContainer.
  1176. //
  1177. //////////
  1178.  
  1179. QTAtomContainer QTEffects_CreateEffectDescription (OSType theEffectName, OSType theSourceName1, OSType theSourceName2)
  1180. {
  1181.     QTAtomContainer        myEffectDesc = NULL;
  1182.     OSType                myType;
  1183.     OSErr                myErr = noErr;
  1184.  
  1185.     // create a new, empty effect description
  1186.     myErr = QTNewAtomContainer(&myEffectDesc);
  1187.     if (myErr != noErr)
  1188.         goto bail;
  1189.  
  1190.     // create the effect ID atom: the atom type is kParameterWhatName, and the atom ID is kParameterWhatID
  1191.     myType = EndianU32_NtoB(theEffectName);
  1192.     myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, 0, sizeof(myType), &myType, NULL);
  1193.     if (myErr != noErr)
  1194.         goto bail;
  1195.         
  1196.     // add the first source, if it's not kSourceNoneName
  1197.     if (theSourceName1 != kSourceNoneName) {
  1198.         myType = EndianU32_NtoB(theSourceName1);
  1199.         myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myType), &myType, NULL);
  1200.         if (myErr != noErr)
  1201.             goto bail;
  1202.     }
  1203.                             
  1204.     // add the second source, if it's not kSourceNoneName
  1205.     if (theSourceName2 != kSourceNoneName) {
  1206.         myType = EndianU32_NtoB(theSourceName2);
  1207.         myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myType), &myType, NULL);
  1208.     }
  1209.  
  1210. bail:
  1211.     return(myEffectDesc);
  1212. }
  1213.  
  1214.  
  1215. //////////
  1216. //
  1217. // QTEffects_AddTrackReferenceToInputMap
  1218. // Add a track reference to the specified input map.
  1219. // 
  1220. //////////
  1221.  
  1222. OSErr QTEffects_AddTrackReferenceToInputMap (QTAtomContainer theInputMap, Track theTrack, Track theSrcTrack, OSType theSrcName)
  1223. {
  1224.     OSErr                myErr = noErr;
  1225.     QTAtom                myInputAtom;
  1226.     long                myRefIndex;
  1227.     OSType                myType;
  1228.  
  1229.     myErr = AddTrackReference(theTrack, theSrcTrack, kTrackModifierReference, &myRefIndex);
  1230.     if (myErr != noErr)
  1231.         goto bail;
  1232.             
  1233.     // add a reference atom to the input map
  1234.     myErr = QTInsertChild(theInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex, 0, 0, NULL, &myInputAtom);
  1235.     if (myErr != noErr)
  1236.         goto bail;
  1237.     
  1238.     // add two child atoms to the parent reference atom
  1239.     myType = EndianU32_NtoB(kTrackModifierTypeImage);
  1240.     myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myType), &myType, NULL);
  1241.     if (myErr != noErr)
  1242.         goto bail;
  1243.     
  1244.     myType = EndianU32_NtoB(theSrcName);
  1245.     myErr = QTInsertChild(theInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myType), &myType, NULL);
  1246.         
  1247. bail:
  1248.     return(myErr);
  1249. }
  1250.  
  1251.  
  1252. //////////
  1253. //
  1254. // QTEffects_SetUpEffectSequence
  1255. // Set up an effects sequence.
  1256. // 
  1257. //////////
  1258.  
  1259. OSErr QTEffects_SetUpEffectSequence (void)
  1260. {
  1261.     OSErr                        myErr = noErr;
  1262.     ImageSequenceDataSource        mySrc1 = 0;
  1263.     ImageSequenceDataSource        mySrc2 = 0;
  1264.     ImageSequenceDataSource        mySrc3 = 0;
  1265.     PixMapHandle                mySrcPixMap;
  1266.     PixMapHandle                myDstPixMap;
  1267.      
  1268.     // if an effect sequence is already set up, end it
  1269.     if (gCurrentState.fEffectSequenceID != 0L) {
  1270.         CDSequenceEnd(gCurrentState.fEffectSequenceID);
  1271.         gCurrentState.fEffectSequenceID = 0L;
  1272.     }
  1273.     
  1274.     // if there is a timebase already set up, dispose of it
  1275.     if (gCurrentState.fTimeBase != NULL) {
  1276.         DisposeTimeBase(gCurrentState.fTimeBase);
  1277.         gCurrentState.fTimeBase = NULL;
  1278.     }
  1279.         
  1280.     // make an effects sequence
  1281.     HLock((Handle)gCurrentState.fEffectDescription);
  1282.  
  1283.     // prepare the decompression sequence for playback
  1284.     myErr = DecompressSequenceBeginS(
  1285.                             &gCurrentState.fEffectSequenceID,
  1286.                             gCurrentState.fSampleDescription,
  1287.                             StripAddress(*gCurrentState.fEffectDescription),
  1288.                             GetHandleSize(gCurrentState.fEffectDescription),
  1289.                             (CGrafPtr)gMainWindow,
  1290.                             NULL,
  1291.                             NULL,
  1292.                             NULL,
  1293.                             ditherCopy,
  1294.                             NULL,
  1295.                             0,
  1296.                             codecNormalQuality,
  1297.                             NULL);
  1298.  
  1299.     HUnlock((Handle)gCurrentState.fEffectDescription);
  1300.     if (myErr != noErr)
  1301.         goto bail;
  1302.  
  1303.     // get the pixel maps for the GWorlds
  1304.     mySrcPixMap = GetGWorldPixMap(gGW1);
  1305.     myDstPixMap = GetGWorldPixMap(gGW2);
  1306.     
  1307.     if ((mySrcPixMap == NULL) || (myDstPixMap == NULL))
  1308.         goto bail;
  1309.  
  1310.     // make the first effect source
  1311.     if (gGW1 == NULL)
  1312.         goto bail;
  1313.     myErr = MakeImageDescriptionForPixMap(mySrcPixMap, &gGW1Desc);
  1314.     if (myErr != noErr)
  1315.         goto bail;
  1316.  
  1317.     myErr = CDSequenceNewDataSource(gCurrentState.fEffectSequenceID, &mySrc1, kSourceOneName, 1, (Handle)gGW1Desc, NULL, 0);
  1318.     if (myErr != noErr)
  1319.         goto bail;
  1320.  
  1321.     CDSequenceSetSourceData(mySrc1, GetPixBaseAddr(mySrcPixMap), (**gGW1Desc).dataSize);
  1322.  
  1323.     // make the second effect source
  1324.     if (gGW2 == NULL)
  1325.         goto bail;
  1326.     myErr = MakeImageDescriptionForPixMap(myDstPixMap, &gGW2Desc);
  1327.     if (myErr != noErr)
  1328.         goto bail;
  1329.  
  1330.     myErr = CDSequenceNewDataSource(gCurrentState.fEffectSequenceID, &mySrc2, kSourceTwoName, 1, (Handle)gGW2Desc, NULL, 0);
  1331.     if (myErr != noErr)
  1332.         goto bail;
  1333.  
  1334.     CDSequenceSetSourceData(mySrc2, GetPixBaseAddr(myDstPixMap), (**gGW2Desc).dataSize);
  1335.  
  1336.     // create a new time base and associate it with the decompression sequence
  1337.     gCurrentState.fTimeBase = NewTimeBase();
  1338.     myErr = GetMoviesError();
  1339.     if (myErr != noErr)
  1340.         goto bail;
  1341.  
  1342.     SetTimeBaseRate(gCurrentState.fTimeBase, 0);
  1343.     myErr = CDSequenceSetTimeBase(gCurrentState.fEffectSequenceID, gCurrentState.fTimeBase);
  1344.  
  1345. bail:
  1346.     return(myErr);
  1347. }
  1348.  
  1349.  
  1350. //////////
  1351. //
  1352. // QTEffects_MakeSampleDescription
  1353. // Return a new image description with default and specified values.
  1354. // 
  1355. //////////
  1356.  
  1357. ImageDescriptionHandle QTEffects_MakeSampleDescription (OSType theEffectType, short theWidth, short theHeight)
  1358. {
  1359.     ImageDescriptionHandle        mySampleDesc = NULL;
  1360.  
  1361. #if USES_MAKE_IMAGE_DESC_FOR_EFFECT
  1362.     OSErr                        myErr = noErr;
  1363.     
  1364.     // create a new sample description
  1365.     myErr = MakeImageDescriptionForEffect(theEffectType, &mySampleDesc);
  1366.     if (myErr != noErr)
  1367.         return(NULL);
  1368. #else
  1369.     // create a new sample description
  1370.     mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  1371.     if (mySampleDesc == NULL)
  1372.         return(NULL);
  1373.         
  1374.     // fill in the fields of the sample description
  1375.     (**mySampleDesc).cType = theEffectType;
  1376.     (**mySampleDesc).idSize = sizeof(ImageDescription);
  1377.     (**mySampleDesc).hRes = 72L << 16;
  1378.     (**mySampleDesc).vRes = 72L << 16;
  1379.     (**mySampleDesc).frameCount = 1;
  1380.     (**mySampleDesc).depth = 0;
  1381.     (**mySampleDesc).clutID = -1;
  1382. #endif
  1383.     
  1384.     (**mySampleDesc).vendor = kAppleManufacturer;
  1385.     (**mySampleDesc).temporalQuality = codecNormalQuality;
  1386.     (**mySampleDesc).spatialQuality = codecNormalQuality;
  1387.     (**mySampleDesc).width = theWidth;
  1388.     (**mySampleDesc).height = theHeight;
  1389.     
  1390.     return(mySampleDesc);
  1391. }
  1392.  
  1393.  
  1394. //////////
  1395. //
  1396. // QTEffects_RunEffect
  1397. // Run the effect: decompress a single step of the effect sequence.
  1398. // 
  1399. //////////
  1400.  
  1401. OSErr QTEffects_RunEffect (TimeValue theTime)
  1402. {
  1403.     OSErr                        myErr = noErr;
  1404.     ICMFrameTimeRecord            myFrameTime;
  1405.  
  1406.     // assertions
  1407.     if ((gCurrentState.fEffectDescription == NULL) || (gCurrentState.fEffectSequenceID == 0L))
  1408.         goto bail;
  1409.  
  1410.     // set the timebase time to the step of the sequence to be rendered
  1411.     SetTimeBaseValue(gCurrentState.fTimeBase, theTime, gNumberOfSteps);
  1412.  
  1413.     myFrameTime.value.hi                = 0;
  1414.     myFrameTime.value.lo                = theTime;
  1415.     myFrameTime.scale                    = gNumberOfSteps;
  1416.     myFrameTime.base                    = 0;
  1417.     myFrameTime.duration                = gNumberOfSteps;
  1418.     myFrameTime.rate                    = 0;
  1419.     myFrameTime.recordSize                = sizeof(myFrameTime);
  1420.     myFrameTime.frameNumber                = 1;
  1421.     myFrameTime.flags                    = icmFrameTimeHasVirtualStartTimeAndDuration;
  1422.     myFrameTime.virtualStartTime.lo        = 0;
  1423.     myFrameTime.virtualStartTime.hi        = 0;
  1424.     myFrameTime.virtualDuration            = gNumberOfSteps;
  1425.     
  1426.     HLock((Handle)gCurrentState.fEffectDescription);
  1427.  
  1428.     myErr = DecompressSequenceFrameWhen(
  1429.                                         gCurrentState.fEffectSequenceID,
  1430.                                         StripAddress(*((Handle)gCurrentState.fEffectDescription)),
  1431.                                         GetHandleSize((Handle)gCurrentState.fEffectDescription),
  1432.                                         0,
  1433.                                         0,
  1434.                                         NULL,
  1435.                                         &myFrameTime);
  1436.                                         
  1437.     HUnlock((Handle)gCurrentState.fEffectDescription);
  1438.     
  1439.     if (myErr != noErr)
  1440.         goto bail;
  1441.     
  1442. bail:
  1443.     return(myErr);
  1444. }
  1445.  
  1446.  
  1447. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1448. //
  1449. // General imaging utilities.
  1450. //
  1451. // Use these functions to draw pictures into GWorlds and do other imaging operations.
  1452. //
  1453. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1454.  
  1455. //////////
  1456. //
  1457. // QTEffects_GetPictResourceAsGWorld
  1458. // Create a new GWorld of the specified size and bit depth; then draw the specified PICT resource into it.
  1459. // The new GWorld is returned through the theGW parameter.
  1460. //
  1461. //////////
  1462.  
  1463. OSErr QTEffects_GetPictResourceAsGWorld (short theResID, short theWidth, short theHeight, short theDepth, GWorldPtr *theGW)
  1464. {
  1465.     PicHandle                myHandle = NULL;
  1466.     PixMapHandle            myPixMap = NULL;
  1467.     CGrafPtr                mySavedPort;
  1468.     GDHandle                mySavedDevice;
  1469.     Rect                    myRect;
  1470.     OSErr                    myErr = noErr;
  1471.  
  1472.     // get the current drawing environment
  1473.     GetGWorld(&mySavedPort, &mySavedDevice);
  1474.  
  1475.     // read the specified PICT resource from the application's resource file
  1476.     myHandle = GetPicture(theResID);
  1477.     if (myHandle == NULL) {
  1478.         myErr = ResError();
  1479.         if (myErr == noErr)
  1480.             myErr = resNotFound;
  1481.         goto bail;
  1482.     }
  1483.  
  1484.     // set the size of the GWorld
  1485.     MacSetRect(&myRect, 0, 0, theWidth, theHeight);
  1486.  
  1487.     // allocate a new GWorld
  1488.     myErr = QTNewGWorld(theGW, theDepth, &myRect, NULL, NULL, kICMTempThenAppMemory);
  1489.     if (myErr != noErr)
  1490.         goto bail;
  1491.     
  1492.     SetGWorld(*theGW, NULL);
  1493.  
  1494.     // get a handle to the offscreen pixel image and lock it
  1495.     myPixMap = GetGWorldPixMap(*theGW);
  1496.     LockPixels(myPixMap);
  1497.  
  1498.     EraseRect(&myRect);
  1499.     DrawPicture(myHandle, &myRect);
  1500.     
  1501.     if (myPixMap != NULL)
  1502.         UnlockPixels(myPixMap);
  1503.     
  1504. bail:
  1505.     // restore the previous port and device
  1506.     SetGWorld(mySavedPort, mySavedDevice);
  1507.  
  1508.     if (myHandle != NULL)
  1509.         ReleaseResource((Handle)myHandle);
  1510.     
  1511.     return(myErr);
  1512. }
  1513.  
  1514.  
  1515. //////////
  1516. //
  1517. // QTEffects_GetPictureAsGWorld
  1518. // Prompt the user to select a picture, create a new GWorld of the specified size and bit depth,
  1519. // and then draw the picture into it. The new GWorld is returned through the theGW parameter.
  1520. //
  1521. //////////
  1522.  
  1523. OSErr QTEffects_GetPictureAsGWorld (short theWidth, short theHeight, short theDepth, GWorldPtr *theGW)
  1524. {
  1525.     SFTypeList                myTypeList;
  1526.     StandardFileReply        myReply;
  1527.     GraphicsImportComponent    myImporter = NULL;
  1528.     Rect                    myRect;
  1529.     OSErr                    myErr = paramErr;
  1530.  
  1531.     // have the user select an image file;
  1532.     // kQTFileTypeQuickTimeImage means any image file readable by GetGraphicsImporterForFile
  1533.     myTypeList[0] = kQTFileTypeQuickTimeImage;
  1534.  
  1535.     StandardGetFilePreview(NULL, 1, myTypeList, &myReply);
  1536.     if (!myReply.sfGood)
  1537.         goto bail;
  1538.         
  1539.     // get a graphics importer for the image file
  1540.     myErr = GetGraphicsImporterForFile(&myReply.sfFile, &myImporter);
  1541.     if (myErr != noErr)
  1542.         goto bail;
  1543.  
  1544.     if (*theGW != NULL) {
  1545.         DisposeGWorld(*theGW);
  1546.         *theGW = NULL;
  1547.     }
  1548.     
  1549.     // set the size of the GWorld
  1550.     MacSetRect(&myRect, 0, 0, theWidth, theHeight);
  1551.  
  1552.     // allocate a new GWorld
  1553.     myErr = QTNewGWorld(theGW, theDepth, &myRect, NULL, NULL, kICMTempThenAppMemory);
  1554.     if (myErr != noErr)
  1555.         goto bail;
  1556.     
  1557.     // draw the picture into the GWorld
  1558.     GraphicsImportSetGWorld(myImporter, *theGW, NULL);
  1559.     GraphicsImportSetBoundsRect(myImporter, &myRect);
  1560.     GraphicsImportDraw(myImporter);
  1561.  
  1562. bail:
  1563.     if (myImporter != NULL)
  1564.         CloseComponent(myImporter);
  1565.     
  1566.     return(myErr);
  1567. }
  1568.  
  1569.  
  1570. //////////
  1571. //
  1572. // QTEffects_AddVideoTrackFromGWorld
  1573. // Add to the specified movie a video track for the specified picture resource.
  1574. //
  1575. //////////
  1576.  
  1577. OSErr QTEffects_AddVideoTrackFromGWorld (Movie *theMovie, GWorldPtr theGW, Track *theSourceTrack, long theStartTime, short theWidth, short theHeight)
  1578. {
  1579.     Media                        myMedia;
  1580.     ImageDescriptionHandle        myDesc = NULL;
  1581.     Rect                        myRect;
  1582.     long                        mySize;
  1583.     Handle                        myData = NULL;
  1584.     Ptr                            myDataPtr = NULL;
  1585.     GWorldPtr                    myGWorld = NULL;
  1586.     CGrafPtr                     mySavedPort = NULL;
  1587.     GDHandle                     mySavedGDevice = NULL;
  1588.     PicHandle                    myHandle = NULL;
  1589.     PixMapHandle                mySrcPixMap = NULL;
  1590.     PixMapHandle                myDstPixMap = NULL;
  1591.     OSErr                        myErr = noErr;
  1592.     
  1593.     // get the current port and device
  1594.     GetGWorld(&mySavedPort, &mySavedGDevice);
  1595.     
  1596.     // create a video track in the movie
  1597.     *theSourceTrack = NewMovieTrack(*theMovie, FixRatio(theWidth, 1), FixRatio(theHeight, 1), kNoVolume);
  1598.     myMedia = NewTrackMedia(*theSourceTrack, VideoMediaType, kOneSecond, NULL, 0);
  1599.     
  1600.     // get the rectangle for the movie
  1601.     GetMovieBox(*theMovie, &myRect);
  1602.     
  1603.     // begin editing the new track
  1604.     BeginMediaEdits(myMedia);
  1605.         
  1606.     // create a new GWorld; we draw the picture into this GWorld and then compress it
  1607.     // (note that we are creating a picture with the maximum bit depth)
  1608.     myErr = NewGWorld(&myGWorld, 32, &myRect, NULL, NULL, 0L);
  1609.     if (myErr != noErr)
  1610.         goto bail;
  1611.     
  1612.     mySrcPixMap = GetGWorldPixMap(theGW);
  1613.     // LockPixels(mySrcPixMap);
  1614.     
  1615.     myDstPixMap = GetGWorldPixMap(myGWorld);
  1616.     LockPixels(myDstPixMap);
  1617.     
  1618.     // create a new image description; CompressImage will fill in the fields of this structure
  1619.     myDesc = (ImageDescriptionHandle)NewHandle(4);
  1620.     
  1621.     SetGWorld(myGWorld, NULL);
  1622.     
  1623.     // copy the image from the specified GWorld into the new GWorld
  1624.     CopyBits((BitMap *)(*mySrcPixMap), (BitMap *)(*myDstPixMap), &(theGW->portRect), &(myGWorld->portRect), srcCopy, 0L);
  1625.  
  1626.     // restore the original port and device
  1627.     SetGWorld(mySavedPort, mySavedGDevice);
  1628.     
  1629.     myErr = GetMaxCompressionSize(myDstPixMap, &myRect, 0, codecNormalQuality, kAnimationCodecType, anyCodec, &mySize);
  1630.     if (myErr != noErr)
  1631.         goto bail;
  1632.         
  1633.     myData = NewHandle(mySize);
  1634.     if (myData == NULL)
  1635.         goto bail;
  1636.         
  1637.     HLockHi(myData);
  1638.     myDataPtr = StripAddress(*myData);
  1639.     
  1640.     myErr = CompressImage(myDstPixMap, &myRect, codecNormalQuality, kAnimationCodecType, myDesc, myDataPtr);
  1641.     if (myErr != noErr)
  1642.         goto bail;
  1643.         
  1644.     myErr = AddMediaSample(myMedia, myData, 0, (**myDesc).dataSize, kEffectMovieDuration, (SampleDescriptionHandle)myDesc, 1, 0, NULL);
  1645.     if (myErr != noErr)
  1646.         goto bail;
  1647.  
  1648.     myErr = EndMediaEdits(myMedia);
  1649.     if (myErr != noErr)
  1650.         goto bail;
  1651.  
  1652.     myErr = InsertMediaIntoTrack(*theSourceTrack, theStartTime, 0, GetMediaDuration(myMedia), fixed1);
  1653.     
  1654. bail:
  1655.     // restore the original port and device
  1656.     SetGWorld(mySavedPort, mySavedGDevice);
  1657.     
  1658.     if (myData != NULL) {
  1659.         HUnlock(myData);
  1660.         DisposeHandle(myData);
  1661.     }
  1662.  
  1663.     if (myDesc != NULL)
  1664.         DisposeHandle((Handle)myDesc);
  1665.         
  1666.     // if (mySrcPixMap != NULL)
  1667.     //     UnlockPixels(mySrcPixMap);
  1668.         
  1669.     if (myDstPixMap != NULL)
  1670.         UnlockPixels(myDstPixMap);
  1671.         
  1672.     if (myGWorld != NULL)
  1673.         DisposeGWorld(myGWorld);
  1674.     
  1675.     return(myErr);
  1676. }
  1677.  
  1678.  
  1679. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1680. //
  1681. // Movie utilities.
  1682. //
  1683. // Use these functions to create movie files and do other movie operations.
  1684. //
  1685. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1686.  
  1687. //////////
  1688. //
  1689. // QTEffects_CreateEffectsMovie
  1690. // Create a movie containing the current video effect transition from one picture to another.
  1691. //
  1692. //////////
  1693.  
  1694. void QTEffects_CreateEffectsMovie (OSType theEffectType, QTAtomContainer theEffectDesc, short theWidth, short theHeight)
  1695. {
  1696.     ImageDescriptionHandle    mySampleDesc = NULL;
  1697.     short                    myResRefNum = 0;
  1698.     short                    myResID = movieInDataForkResID;
  1699.     Movie                    myMovie = NULL;
  1700.     Track                    myTrack = NULL;
  1701.     Track                    mySrc1Track = NULL;
  1702.     Track                    mySrc2Track = NULL;
  1703.     Media                    myMedia;
  1704.     StandardFileReply        myReply;
  1705.     QTAtomContainer            myInputMap = NULL;
  1706.     TimeValue                mySampleTime;
  1707.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  1708.     StringPtr                 myMoviePrompt = QTUtils_ConvertCToPascalString(kSaveEffectMoviePrompt);
  1709.     StringPtr                 myMovieFileName = QTUtils_ConvertCToPascalString(kSaveEffectMovieFileName);
  1710.     OSErr                    myErr = noErr;
  1711.     
  1712.     // create an effect sample description
  1713.     mySampleDesc = QTEffects_MakeSampleDescription(theEffectType, theWidth, theHeight);
  1714.     if (mySampleDesc == NULL)
  1715.         goto bail;
  1716.  
  1717.     // prompt user for new file name
  1718.     StandardPutFile(myMoviePrompt, myMovieFileName, &myReply);
  1719.     if (!myReply.sfGood)
  1720.         goto bail;                // deal with user cancelling
  1721.  
  1722.     // create a movie file for the destination movie
  1723.     myErr = CreateMovieFile(&myReply.sfFile, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myMovie);
  1724.     if (myErr != noErr)
  1725.         goto bail;
  1726.     
  1727.     // add the video tracks of the source pictures to the effects movie;
  1728.     // the video tracks used as sources for the effect should start at the same time as the effect track
  1729.     // and end at the same time as the effect track
  1730.     myErr = QTEffects_AddVideoTrackFromGWorld(&myMovie, gGW1, &mySrc1Track, 0, theWidth, theHeight);
  1731.     if (myErr != noErr)
  1732.         goto bail;
  1733.         
  1734.     myErr = QTEffects_AddVideoTrackFromGWorld(&myMovie, gGW2, &mySrc2Track, 0, theWidth, theHeight);
  1735.     if (myErr != noErr)
  1736.         goto bail;
  1737.  
  1738.     // create the video effect track and media
  1739.     myTrack = NewMovieTrack(myMovie, FixRatio(theWidth, 1), FixRatio(theHeight, 1), kNoVolume);
  1740.     myMedia = NewTrackMedia(myTrack, VideoMediaType, kOneSecond, NULL, 0);
  1741.  
  1742.     // add the effect description as a sample to the effect track media
  1743.     BeginMediaEdits(myMedia);
  1744.  
  1745.     myErr = AddMediaSample(myMedia, (Handle)theEffectDesc, 0, GetHandleSize((Handle)theEffectDesc), kEffectMovieDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, &mySampleTime);
  1746.     if (myErr != noErr)
  1747.         goto bail;
  1748.  
  1749.     EndMediaEdits(myMedia);
  1750.     
  1751.     // create the input map and add references for the two video tracks
  1752.     myErr = QTNewAtomContainer(&myInputMap);
  1753.     if (myErr != noErr)
  1754.         goto bail;
  1755.         
  1756.     myErr = QTEffects_AddTrackReferenceToInputMap(myInputMap, myTrack, mySrc1Track, kSourceOneName);
  1757.     if (myErr != noErr)
  1758.         goto bail;
  1759.         
  1760.     myErr = QTEffects_AddTrackReferenceToInputMap(myInputMap, myTrack, mySrc2Track, kSourceTwoName);
  1761.     if (myErr != noErr)
  1762.         goto bail;
  1763.  
  1764.     // add the input map to the effects track
  1765.     myErr = SetMediaInputMap(myMedia, myInputMap);
  1766.     if (myErr != noErr)
  1767.         goto bail;
  1768.  
  1769.     // add the media to the track
  1770.     myErr = InsertMediaIntoTrack(myTrack, 0, mySampleTime, GetMediaDuration(myMedia), fixed1);
  1771.     if (myErr != noErr)
  1772.         goto bail;
  1773.         
  1774. #if ALLOW_COMPOUND_EFFECTS
  1775.     QTEffects_AddFilmNoiseToMovie(myMovie, myTrack);
  1776. #endif
  1777.  
  1778. #ifdef __QTUtilities__
  1779.     // save the current looping state
  1780.     myErr = QTUtils_SetMovieFileLoopingInfo(myMovie, (gLoopingState - (kSettingsMenu << 8)) - 2);
  1781. #endif
  1782.  
  1783.     // put the movie resource into the file
  1784.     myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);
  1785.     
  1786. bail:        
  1787.     if (mySampleDesc != NULL)
  1788.         DisposeHandle((Handle)mySampleDesc);
  1789.     
  1790.     if (myResRefNum != 0)
  1791.         CloseMovieFile(myResRefNum);
  1792.  
  1793.     if (myMovie != NULL)
  1794.         DisposeMovie(myMovie);
  1795.  
  1796.     if (myInputMap != NULL)
  1797.         QTDisposeAtomContainer(myInputMap);
  1798.     
  1799.     free(myMoviePrompt);
  1800.     free(myMovieFileName);
  1801.  
  1802.     return;
  1803. }
  1804.  
  1805.  
  1806. //////////
  1807. //
  1808. // QTEffects_AddFilmNoiseToMovie
  1809. // Add the film noise filter to the specified track.
  1810. //
  1811. //////////
  1812.  
  1813. void QTEffects_AddFilmNoiseToMovie (Movie theMovie, Track theSrcTrack)
  1814. {
  1815.     ImageDescriptionHandle    mySampleDesc = NULL;
  1816.     Track                    myTrack = NULL;
  1817.     Media                    myMedia = NULL;
  1818.     QTAtomContainer            myInputMap = NULL;
  1819.     QTAtomContainer            myEffectDesc = NULL;
  1820.     TimeValue                mySampleTime;
  1821.     Fixed                    myWidth, myHeight;
  1822.     OSErr                    myErr = noErr;
  1823.  
  1824.     // get width and height of track
  1825.     GetTrackDimensions(theSrcTrack, &myWidth, &myHeight);    
  1826.     
  1827.     // create an effect sample description
  1828.     mySampleDesc = QTEffects_MakeSampleDescription(kFilmNoiseImageFilterType, myWidth >> 16, myHeight >> 16);
  1829.     if (mySampleDesc == NULL)
  1830.         goto bail;
  1831.  
  1832.     // create the video effect track and media
  1833.     myTrack = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume);
  1834.     myMedia = NewTrackMedia(myTrack, VideoMediaType, kOneSecond, NULL, 0);
  1835.  
  1836.     // create an effect description
  1837.     myEffectDesc = QTEffects_CreateEffectDescription(kFilmNoiseImageFilterType, kSourceThreeName, kSourceNoneName);
  1838.  
  1839.     // add the effect description as a sample to the effect track media
  1840.     BeginMediaEdits(myMedia);
  1841.  
  1842.     myErr = AddMediaSample(myMedia, (Handle)myEffectDesc, 0, GetHandleSize((Handle)myEffectDesc), kEffectMovieDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, &mySampleTime);
  1843.     if (myErr != noErr)
  1844.         goto bail;
  1845.  
  1846.     EndMediaEdits(myMedia);
  1847.     
  1848.     // create the input map and add references for the first effect track
  1849.     myErr = QTNewAtomContainer(&myInputMap);
  1850.     if (myErr != noErr)
  1851.         goto bail;
  1852.         
  1853.     myErr = QTEffects_AddTrackReferenceToInputMap(myInputMap, myTrack, theSrcTrack, kSourceThreeName);
  1854.     if (myErr != noErr)
  1855.         goto bail;
  1856.         
  1857.     // add the input map to the effects track
  1858.     myErr = SetMediaInputMap(myMedia, myInputMap);
  1859.     if (myErr != noErr)
  1860.         goto bail;
  1861.  
  1862.     // add the media to the track
  1863.     myErr = InsertMediaIntoTrack(myTrack, 0, mySampleTime, GetMediaDuration(myMedia), fixed1);
  1864.     if (myErr != noErr)
  1865.         goto bail;
  1866.     
  1867. bail:        
  1868.     if (mySampleDesc != NULL)
  1869.         DisposeHandle((Handle)mySampleDesc);
  1870.     
  1871.     if (myInputMap != NULL)
  1872.         QTDisposeAtomContainer(myInputMap);
  1873.     
  1874.     return;
  1875. }
  1876.